package com.yst.web.utils;


import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class BeanUtils {
    /**
     * dynamic set object's field value
     * @param fieldName field name
     * @param object dest object
     * @param value value
     * @throws Exception exception
     */
    public static void setFieldValue(String fieldName,Object object,Object value) throws Exception {
        try {
            try {
                Class<?> clazz=object.getClass();
                Field field=ClassUtils.getClassFields(clazz).get(fieldName);
                while(Objects.isNull(field)){
                    clazz=clazz.getSuperclass();
                    field=ClassUtils.getClassFields(clazz).get(fieldName);
                    if (clazz==Object.class){
                        break;
                    }
                }
                if (Objects.nonNull(field)) {
                    field.setAccessible(true);
                    field.set(object, value);
                    return;
                }
            }
            catch (Exception ignore){
            }

            String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            object.getClass().getMethod(methodName,value.getClass()).invoke(object, value);
        }
        catch (Exception e){
            throw new Exception(String.format("dynamic set object field value fail! field:%s,class:%s",
                    fieldName,object.getClass().getName()));
        }
    }


    /**
     * dynamic get object's field value
     * @param object object
     * @param fieldName name of field
     * @param defaultValue default value
     * @return value of field
     */
    public static Object getFieldValue(Object object,String fieldName,Object defaultValue){
        try {
            Object result = null;
            try {
                Field field=ClassUtils.getClassFields(object.getClass()).get(fieldName);
                field.setAccessible(true);
                result=field.get(object);
                if (Objects.nonNull(result)) {
                    return result;
                }
                return defaultValue;
            }
            catch (Exception ignore){
               //ignore
            }

            try {
                Field field=object.getClass().getDeclaredField(fieldName);
                result=field.get(object);
                if (Objects.nonNull(result)) {
                    return result;
                }
                return defaultValue;
            }
            catch (Exception ignore){
                //ignore
            }

            String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Method method=object.getClass().getMethod(methodName);
            result=method.invoke(object);

            if (Objects.nonNull(result))
            {
                return result;
            }

            return defaultValue;
        }
        catch (Exception e){
            return defaultValue;
        }
    }

    /**
     * class of field value
     * @param object object
     * @param fieldName name of field
     * @param defaultClass default class of field
     * @return class of field
     */
    public static Class<?> getFieldClass(Object object, String fieldName, Class<?> defaultClass){
        return getFieldClass(object.getClass(),fieldName,defaultClass);
    }

    /**
     * fetch type of field
     * @param clazz owner class of field
     * @param fieldName name of field
     * @param defaultClass default value
     * @return class of field
     */
    public static Class<?> getFieldClass(Class<?> clazz, String fieldName, Class<?> defaultClass){
        try {
            try {
                Field field=clazz.getField(fieldName);
                return field.getType();
            }
            catch (Exception ignore){
                //ignore
            }

            try {
                String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                Method method = clazz.getMethod(methodName);
                return method.getReturnType();
            }
            catch (Exception ignore){
                //ignore
            }

            return defaultClass;
        }
        catch (Exception e){
            return defaultClass;
        }
    }

    public static void copyProperties(Object source,Object target,String...ignoreFields) throws Exception {
        Map<String,Field> fieldMap=ClassUtils.getClassFields(target.getClass());
        if (Objects.isNull(fieldMap)){
            throw new RuntimeException("can not fetch class field "+target.getClass().getName());
        }
        List<String> ifs= Arrays.asList(ignoreFields);
        for(String fieldName:fieldMap.keySet()){
            if (!ifs.contains(fieldName)) {
                setFieldValue(fieldName,target,getFieldValue(source, fieldName, null));
            }
        }
    }

    public static void mapToBean(Map<String,Object> map,Object bean,String...ignoreFields) throws Exception{
        Map<String,Field> fieldMap=ClassUtils.getClassFields(bean.getClass());
        List<String> delFieldList=Arrays.asList(ignoreFields);
        Set<String> allFieldSet=new HashSet<>(fieldMap.keySet());
        delFieldList.forEach(allFieldSet::remove);
        for (String key:allFieldSet){
            if (map.containsKey(key)){
                setFieldValue(key,bean,map.get(key));
            }
        }
    }
}
